<template>
  <div class="inline-edit" @click.stop="handleClick" ref="wrapper">
    <!-- {{ isEditing }} {{ isOutSide }} -->
    <input
      v-model="innerValue"
      v-if="isEditing"
      placeholder="文本不能为空"
      ref="inputRef" 
      v-bind="$attrs"
    />
    <slot v-else :text="innerValue">
      <span v-bind="$attrs">{{ innerValue }}</span>
    </slot>
  </div>
</template>
<script lang="ts" setup>
import {
  ref,
  nextTick,
  defineProps,
  defineEmits,
  watch,
  withDefaults,
} from "vue";
import useKeyPress from "@/hooks/useKeyPress";
import useClickOutside from "@/hooks/useClickOutside";

const props = withDefaults(
  defineProps<{
    value?: string;
  }>(),
  {
    value: "",
  }
);

const emits = defineEmits<{
  (e: "change", value: string): void;
}>();

const isEditing = ref(false);
const innerValue = ref(props.value);
// 这里需要监听,不然innerValue不会改变, 因为是值传递, 但是props.value不好直接用于v-model
watch(() => props.value, (newValue) => {
  if (newValue) {
    innerValue.value = newValue;
  }
});

let cachedOldValue = "";

const wrapper = ref<null | HTMLElement>(null);
//是否编辑时点到了外面 不能用失去焦点事件么
const isOutSide = useClickOutside(wrapper);
const inputRef = ref<null | HTMLInputElement>(null);

const handleClick = () => {
  isEditing.value = true;
};

watch(isEditing, async (newValue) => {
  if (newValue) {
    cachedOldValue = innerValue.value;
    nextTick(() => {
      if (inputRef.value) {
        inputRef.value.focus();
      }
    });
  }
});

watch(isOutSide, (newValue) => {
  if (newValue && isEditing.value) {
    isEditing.value = false;
    emits("change", innerValue.value);
  }
  // handleClick配置了阻止冒泡, 会导致input区域无法监测
  // 可以尝试editor默认设置为layer,可以看到点击外部为true后,就不会更新了
  // 所以这里可以默认设置为false,点击外部会变成true就解决问题了
  // 另外去除冒泡会有另外的bug
  isOutSide.value = false;
});

useKeyPress("Enter", () => {
  if (isEditing.value) {
    isEditing.value = false;
    emits("change", innerValue.value);
  }
});
useKeyPress("Escape", () => {
  if (isEditing.value) {
    isEditing.value = false;
    innerValue.value = cachedOldValue;
  }
});
</script>
<style scoped lang="less"></style>
